home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************************************
- *
- *
- * MacZoop - "the framework for the rest of us"
- *
- *
- *
- * ZGIFFile.cpp -- a file object that can open GIF images
- *
- *
- *
- *
- *
- * © 1996, Graham Cox
- *
- *
- *
- *
- *************************************************************************************************/
-
-
-
- #include "ZGIFFile.h"
- #include "ZGWorld.h"
- #include "MacZoop.h"
-
-
- static OSErr ParseGIF( short refNum, GWorldPtr *world );
-
- /*---------------------------------*** CONSTRUCTOR ***----------------------------------*/
-
-
- ZGIFFile::ZGIFFile( const FSSpec& aSpec )
- : ZFile( aSpec )
- {
- }
-
- /*------------------------------------*** READ ***--------------------------------------*/
- /*
- read the GIF file into a ZGWorld object
- ----------------------------------------------------------------------------------------*/
-
- void ZGIFFile::Read( ZGWorld* aGWorld )
- {
- GWorldPtr gw;
-
- FailNILParam( aGWorld );
- if ( refNum == _NOT_OPEN )
- FailOSErr( fnOpnErr );
-
- gw = aGWorld->GetMacGWorld();
-
- if ( gw )
- DisposeGWorld( gw );
-
- aGWorld->SetMacGWorld( gw = NULL );
-
- Read( &gw );
-
- aGWorld->SetMacGWorld( gw );
- }
-
-
- /*------------------------------------*** READ ***--------------------------------------*/
- /*
- read the GIF file into a Mac GWorld
- ----------------------------------------------------------------------------------------*/
-
- void ZGIFFile::Read( GWorldPtr* aGWorld )
- {
- FailNILParam( aGWorld );
-
- if ( refNum == _NOT_OPEN )
- FailOSErr( fnOpnErr );
-
- FailOSErr( ParseGIF( refNum, aGWorld ));
- }
-
-
-
- #pragma mark -
- #pragma mark #### GIF Parsing Stuff ####
-
- /*
-
- This code was supplied by James Bunk, though may not have been written by him. Modifications
- to the code to use exceptions for errors, etc. by Graham Cox.
-
- */
-
- #define ColorMapMask 0x80
- #define ImageSeparator 0x2C
- #define ExtensionIntroducer 0x21
- #define NullBlock 0x00
- #define InterlaceMask 0x40
- #define BufferSize 8192 // May be freely modified
-
- #define abort(x) { FailOSErr( x ); }
- #define Check(x) if (e) abort(x) // this means return e
- #define Read(c, buf) count = c; \
- e = FSRead (refNum, &count, buf); /* Read the requested number of bytes */ \
- Check(strCantRead) \
- FileSize -= count /* And note that we have read them */
-
- #define NextByte(b) Read(1, &b)
-
- #define Skip(n) e = SetFPos (refNum, fsFromMark, n); /* Skip n bytes */ \
- Check(strCantSetFPos) \
- FileSize -= n // And note that we have read them */
-
- #define FillBuffer { \
- BufferPointer = BufferBase; \
- Read (BufferSize <= FileSize ? BufferSize : FileSize, BufferBase); \
- }
-
- #define BufferGetOne { \
- if (++BufferPointer >= BufferTop) /* If we are at the end of the buffer */ \
- FillBuffer /* Then fill it again */ \
- if (BlockCount == 0) { /* If the next byte is a length byte */ \
- BlockCount = *(BufferPointer++); /* Then skip it and update BlockCount */ \
- if (BufferPointer == BufferTop) /* If this brings us to the end of the buffer */ \
- FillBuffer /* Then fill it again */ \
- } \
- BlockCount--; \
- }
-
- #define ReadCode(code) { \
- if (BitOffset == 0) BufferGetOne \
- data = *BufferPointer; /* Read the current byte */ \
- newBitOffset = BitOffset + CodeSize; \
- if (newBitOffset > 8) { /* If we need more, read the next byte */ \
- BufferGetOne \
- data += ((long) (*BufferPointer)) << 8; \
- } \
- if (newBitOffset > 16) { /* If we still need more, read a third byte */ \
- BufferGetOne \
- data += ((long) (*BufferPointer)) << 16; \
- } \
- data >>= BitOffset; /* Skip the bits we already processed */ \
- BitOffset = newBitOffset & 7; /* Compute the new bit offset */ \
- code = data & ReadMask; /* Mask off the bits we don't want yet */ \
- }
-
- #define DrawPixel(index) { \
- * (Byte*) curAddr = index; /* Write out the pixel */ \
- curAddr = (long *) ((Byte*) curAddr + 1); /* Update the pointer */ \
- if (++xc == Width) { /* Update the x-coordinate */ \
- xc = 0; /* If it overflows, update the y-coordinate */ \
- if (!Interlaced) /* In a non-interlaced picture, just */ \
- yc++; /* increment yc to the next scan line */ \
- else { \
- switch (Pass) { /* Otherwise deal with the interlace as */ \
- case 0: /* described in the GIF spec */ \
- yc += 8; \
- if (yc >= Height) { Pass++; yc = 4; } \
- break; \
- case 1: \
- yc += 8; \
- if (yc >= Height) { Pass++; yc = 2; } \
- break; \
- case 2: \
- yc += 4; \
- if (yc >= Height) { Pass++; yc = 1; } \
- break; \
- case 3: \
- yc += 2; \
- break; \
- default: \
- break; \
- } \
- } \
- curAddr = (long*) ((long) srcBaseAddr + (long) yc * (long) rowBytes); /* update current address in bitmap */ \
- } \
- }
-
- #define ReadColorMap { \
- Boolean mapPresent; \
- CTabPtr tablePtr; \
- short i; \
- \
- mapPresent = (flags & ColorMapMask) ? true : false; /* Does the file have a global color map ? */ \
- if (mapPresent) { \
- BitsPerPixel = (flags & 7) + 1; /* Image bit depth */ \
- ColorMapSize = 1 << BitsPerPixel; /* Number of entries in color table */ \
- BitMask = ColorMapSize - 1; \
- \
- if (ColorTable != NULL) /* The local table overrides the global one */ \
- DisposeHandle ((Handle) ColorTable); /* so let's get rid of the latter if it exists */ \
- ColorTable = (CTabHandle) NewHandle (8*ColorMapSize+8); /* Allocate memory for the table */ \
- if (ColorTable == NULL) { e = memFullErr; abort(strGIFNoMem) } \
- HLock((Handle) ColorTable); \
- tablePtr = *ColorTable; \
- tablePtr->ctSeed = 0; /* I don't know what to put in there */ \
- tablePtr->ctFlags = 0; /* so I just zero these fields */ \
- tablePtr->ctSize = ColorMapSize - 1; \
- \
- for (i = 0; i < ColorMapSize; i++) { \
- tablePtr->ctTable[i].value = i; \
- NextByte(b); \
- tablePtr->ctTable[i].rgb.red = (short) b * 0x100; /* Determine RGB value */ \
- NextByte(b); \
- tablePtr->ctTable[i].rgb.green = (short) b * 0x100; \
- NextByte(b); \
- tablePtr->ctTable[i].rgb.blue = (short) b * 0x100; \
- } \
- HUnlock((Handle) ColorTable); \
- } \
- HasColorMap = HasColorMap || mapPresent; \
- }
-
-
- /************************************************************************
- ************************************************************************/
-
- static OSErr ParseGIF( short refNum,GWorldPtr *world)
- {
- OSErr e; /* Error code */
- long sig;
- long count,FileSize;
- Byte b, flags, c;
- short BitsPerPixel, ColorMapSize, BitMask;
- short Width, Height;
- short ClearCode, FreeCode, EOFCode, FirstFree;
- Byte CodeSize, InitCodeSize;
- short MaxCode;
- Byte *BufferBase = NULL; /* Read buffer */
- Byte *BufferPointer, *BufferTop; /* Offset in buffer */
- Byte BlockCount; /* Remaining bytes in current block */
- Byte BitOffset, newBitOffset;
- long data;
- short ReadMask; /* Mask with exactly CodeSize bits set to 1 */
- short CurCode, InCode, OldCode, Code; /* Decompressor variables */
- short FinChar;
- short *OutCodeBase = NULL; /* Output array used by the decompressor */
- short *OutCode; /* Current pointer into this array */
- short *Prefix = NULL; /* The hash table used by the decompressor */
- short *Suffix = NULL;
- short xc, yc; /* Pen position */
- short Pass; /* Used by the output routines for interlaced pics */
- Boolean HasColorMap, Interlaced;
- PixMapHandle srcPixMap; /* These are associated with 'world' */
- long *srcBaseAddr, *curAddr; /* Pixmap base address and current address */
- short rowBytes;
- Rect bounds;
- CTabHandle ColorTable = NULL; /* Custom color table */
- GWorldPtr oldWorld; /* Some temporary variables */
- GDHandle oldDevice;
- long progRefNum = 0; // progress bar ref number
- GWorldFlags gwflags;
-
- try
- {
- if (!(OutCodeBase = (short*) NewPtr(2050))) { e = MemError(); abort(strGIFNoMem) }
- if (!(Prefix = (short*) NewPtr(8192))) { e = MemError(); abort(strGIFNoMem) }
- if (!(Suffix = (short*) NewPtr(8192))) { e = MemError(); abort(strGIFNoMem) }
- if (!(BufferBase = (Byte*) NewPtr(BufferSize))) { e = MemError(); abort(strGIFNoMem) }
-
- // get the file size
-
- e = GetEOF(refNum,&FileSize);
-
- Read (4, &sig); /* Read the GIF signature */
- if (sig != 'GIF8') { e = paramErr; abort(strInvalidGIFSig) } /* and make sure it's correct */
- Skip(6); /* Skip screen dimensions */
- NextByte(flags); /* This flag byte is processed below */
- Skip(2); /* Skip background color and aspect ratio */
-
- ReadColorMap /* Read the global color map */
-
- while (TRUE)
- {
- NextByte(b); /* Read the first byte of the block */
-
- switch (b)
- {
- case ImageSeparator: /* Image separator, let's go read the image */
- goto ReadImage;
- case ExtensionIntroducer: /* Extension block */
- Skip(1); /* Skip the extension label */
- NextByte(b); /* Get the block size */
- Skip(b); /* Skip the block */
-
- NextByte(b); /* Read the subsequent data blocks */
- while (b != NullBlock)
- { /* until a trailer block is reached */
- Skip(b); /* Skip this block */
- NextByte(b); /* Get length of the next block */
- }
-
- break;
- default: /* Unknown file format */
- e = paramErr;
- abort(strUnknownGIFBlock);
- }
- }
-
- ReadImage:
- Skip(4); /* Skip left and top offsets */
- NextByte(b); NextByte(c); /* Read width and height */
- Width = (short) c * 0x100 + b; /* Byte per byte because they are written */
- NextByte(b); NextByte(c); /* in stupid PC-like big-endian order */
- Height = (short) c * 0x100 + b;
-
- NextByte(flags);
- Interlaced = (flags & InterlaceMask) ? true : false; /* See if the image is interlaced */
-
- ReadColorMap /* If there is a local color map, read it */
-
- if (!HasColorMap) { e = paramErr; abort(strNoColorMap) } /* Make sure we have at least one color map */
-
- GetGWorld(&oldWorld, &oldDevice); /* save the current world */
- SetRect(&bounds, 0, 0, Width, Height); /* and create our own */
-
- (*ColorTable)->ctSeed = GetCTSeed(); // $$GPC- set some seed value rather than zero (in 7.5.3+,
- // failure to do this will hang the system)
-
- gwflags = 0;
-
- if ( e = NewGWorld( world, 8, &bounds, ColorTable, NULL, gwflags ) )
- abort( strGIFNoMem )
-
- LockPixels(GetGWorldPixMap(*world)); /* Lock the pixel map */
- SetGWorld (*world, NULL); /* activate it */
-
- NextByte(CodeSize);
- ClearCode = (1 << CodeSize);
- EOFCode = ClearCode + 1;
- FreeCode = FirstFree = ClearCode + 2;
-
- CodeSize++;
- InitCodeSize = CodeSize;
- MaxCode = (1 << CodeSize);
- ReadMask = MaxCode - 1;
-
- BlockCount = 0; /* We start at the beginning of a block */
- BitOffset = 0; /* and at the beginning of a byte */
- BufferPointer = BufferTop = BufferBase + BufferSize; /* Force the buffer to be filled immediately */
-
- xc = yc = 0;
- Pass = 0;
-
- srcPixMap = GetGWorldPixMap(*world); /* Get the pixmap */
- rowBytes = (*srcPixMap)->rowBytes & 0x7FFF; /* get rowBytes */
- curAddr = srcBaseAddr = (long*) GetPixBaseAddr (srcPixMap); /* and the base address of the pixmap */
-
- ReadCode(Code);
- while (Code != EOFCode)
- {
- if (Code == ClearCode)
- { /* Clear code sets everything back to its */
- CodeSize = InitCodeSize; /* initial value, then reads the subsequent code */
- MaxCode = (1 << CodeSize); /* as uncompressed data. */
- ReadMask = MaxCode - 1;
- FreeCode = FirstFree;
- ReadCode(Code);
- CurCode = OldCode = Code;
- FinChar = CurCode & BitMask;
- DrawPixel(FinChar);
- }
- else
- { /* If not Clear code, then must be data. */
- CurCode = InCode = Code; /* Save same as CurCode and InCode */
-
- OutCode = OutCodeBase;
- if (CurCode >= FreeCode)
- { /* If >= FreeCode, not in the hash table yet */
- CurCode = OldCode; /* repeat the last character decoded */
- *(OutCode++) = FinChar;
- }
-
- while (CurCode > BitMask)
- { /* Pursue the chain pointed to by CurCode */
- *(OutCode++) = Suffix[CurCode]; /* through the hash table to its end */
- CurCode = Prefix[CurCode]; /* the output queue */
- }
-
- FinChar = CurCode & BitMask; /* The last code in the chain is treated as raw data. */
- *OutCode = FinChar;
-
- while (OutCode >= OutCodeBase) /* Now we put the data out */
- DrawPixel(*(OutCode--)); /* It's been stacked LIFO, so deal with it that way */
-
- Prefix[FreeCode] = OldCode; /* Build the hash table on-the-fly */
- Suffix[FreeCode] = FinChar; /* No table is stored in the file */
- OldCode = InCode;
-
- if (++FreeCode >= MaxCode) /* Point to the next slot in the table */
- if (CodeSize < 12)
- { /* If we exceed the current MaxCode value */
- CodeSize++; /* increment the code size unless it's already 12 */
- MaxCode *= 2; /* If it is, do nothing; the next code better be */
- ReadMask = MaxCode - 1; /* Clear */
- }
- }
-
- ReadCode(Code); /* Read the next code */
- }
-
- UnlockPixels(GetGWorldPixMap(*world)); /* Unlock the pixel map */
- SetGWorld (oldWorld, oldDevice); /* restore the previous graphics world */
-
- e = noErr;
- }
- catch( OSErr err )
- {
- if (ColorTable) /* We can destroy the CTable, NewGWorld */
- DisposeHandle((Handle) ColorTable);
-
- if (BufferBase)
- DisposePtr((Ptr) BufferBase);
-
- if (Suffix)
- DisposePtr((Ptr) Suffix);
-
- if (Prefix)
- DisposePtr((Ptr) Prefix);
-
- if (OutCodeBase)
- DisposePtr((Ptr) OutCodeBase);
-
- throw err;
- }
- end:
- if (ColorTable) /* We can destroy the CTable, NewGWorld */
- DisposeHandle((Handle) ColorTable);
-
- if (BufferBase)
- DisposePtr((Ptr) BufferBase);
-
- if (Suffix)
- DisposePtr((Ptr) Suffix);
-
- if (Prefix)
- DisposePtr((Ptr) Prefix);
-
- if (OutCodeBase)
- DisposePtr((Ptr) OutCodeBase);
-
- return(e);
- }